
#include "chatservice.hpp"
#include <string>
#include <map>
#include <muduo/base/Logging.h>
using namespace muduo;

// 初始化
Chatservice *Chatservice::_instance = nullptr;

// 获得单例对象的接口函数
Chatservice *Chatservice::instance()
{
    // static ChatService service;
    // return &service;
    static pthread_mutex_t mutex_static;
    if (_instance == nullptr)
    {
        // 加锁
        pthread_mutex_lock(&mutex_static);
        if (_instance == nullptr)
        {
            // 调用构造
            _instance = new Chatservice();
        }
        pthread_mutex_unlock(&mutex_static);
    }
    pthread_mutex_destroy(&mutex_static);
    return _instance;
}

// 注册消息以及对应的Handler回调操作
Chatservice::Chatservice()
{
    // 用户基本业务管理相关事件处理回调注册
    //_msgHandlerMap.insert({LOGIN_MSG,login});
    //_msgHandlerMap.insert({REG_MSG,reg});
    _msgHandlerMap.insert({LOGIN_MSG, std::bind(&Chatservice::login, this, _1, _2, _3)});
    _msgHandlerMap.insert({REG_MSG, std::bind(&Chatservice::reg, this, _1, _2, _3)});
    _msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&Chatservice::oneChat, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&Chatservice::addfriend, this, _1, _2, _3)});
    _msgHandlerMap.insert({CREATE_GROUP_MSG, std::bind(&Chatservice::createGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_GROUP_MSG, std::bind(&Chatservice::addGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({SHOW_GROUPS_MSG, std::bind(&Chatservice::showGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({SHOW_FRIENDS_MSG, std::bind(&Chatservice::showfriends, this, _1, _2, _3)});
    _msgHandlerMap.insert({LEAVE_MESSAGE_MSG, std::bind(&Chatservice::leavemessage, this, _1, _2, _3)});
    _msgHandlerMap.insert({GROUP_CHAT_MSG,std::bind(&Chatservice::groupchat,this,_1,_2,_3)});
    _msgHandlerMap.insert({LOGINOUT_MSG,std::bind(&Chatservice::loginout,this,_1,_2,_3)});

    if(_redismodel.connect())
    {
        //设置回调函数
        _redismodel.init_notify_handler(std::bind(&Chatservice::handleRedisSubscribeMessage,this,_1,_2));

    }
}

// 处理登录业务
void Chatservice::login(const TcpConnectionPtr &conn, Json::Value &js, Timestamp time)
{
    // 登录业务需要传入id和密码
    int id = js["id"].asInt();
    string pwd = js["password"].asString();
    User user = _usrmodel.query(id); // 根据用户id查询用户信息
    // 如果查询成功，并且密码正确
    if (user.getid() == id && user.getpassword() == pwd)
    {
        // 重复登录的情况
        if (user.getstate() == "online")
        {

            Json::Value response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 1;
            response["errmsg"] = "该用户已经成功登录，请勿重复登录";
            // Json转字符串
            Json::FastWriter fastwriter;
            string message = fastwriter.write(response);
            conn->send(message);
        }
        else // 正常登录的情况
        {

            // 记录连接映射关系
            //
            {
                // lock_guard类似于智能指针，构造时加锁，出作用域析构自动解锁。
                lock_guard<mutex> lock(_connmutex);
                _userConnMap.insert({id, conn});
            }

            //登录成功，订阅自己id的消息
            _redismodel.subscribe(id);


            // 数据库的并发安全由mysql自己解决
            user.setstate("online");
            // 更新用户的状态
            _usrmodel.UpdateState(user);
            // 登录成功
            Json::Value response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errmsg"] = "登录成功";
            response["errno"] = 0;
            response["id"] = user.getid();
            response["name"] = user.getname();
            // Json转字符串
            Json::FastWriter fastwriter;
            string message = fastwriter.write(response);
            conn->send(message);
        }
    }
    else // 用户不存在或者密码错误的情况
    {
        // 如果密码错误
        if (user.getid() == id)
        {
            Json::Value response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 2;
            response["errmsg"] = "密码错误，请重新登录";
            // Json转字符串
            Json::FastWriter fastwriter;
            string message = fastwriter.write(response);
            conn->send(message);
        }
        else
        {
            Json::Value response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 3;
            response["errmsg"] = "用户不存在，请确定用户id是否正确";
            Json::FastWriter fastwriter;
            string message = fastwriter.write(response);
            conn->send(message);
        }
    }
}

// 处理注册业务
void Chatservice::reg(const TcpConnectionPtr &conn, Json::Value &js, Timestamp time)
{

    Json::FastWriter w;
    string msg = w.write(js);
    // 注册业务需要用户传入： name  password; id和state有默认生成即可
    // 从js对象中获取name password字段
    string name = js["name"].asString();
    // cout<<"name"<<name<<endl;
    string password = js["password"].asString();
    // cout<<"password"<<password<<endl;
    User user;
    user.setname(name);
    user.setpassword(password);
    bool state = _usrmodel.insert(user);
    if (state)
    {
        Json::Value respon;
        respon["msgid"] = REG_MSG_ACK;
        respon["errno"] = 0;
        respon["id"] = user.getid();
        // 将json格式转换为字符串
        Json::FastWriter fastwriter;
        string message = fastwriter.write(respon);

        // 将message返回给客户端
        conn->send(message);
    }
    else
    {
        // 注册失败
        Json::Value respon;
        respon["msgid"] = REG_MSG_ACK;
        respon["errno"] = 1;
        // 将json格式转换为字符串
        Json::FastWriter fastwriter;
        string message = fastwriter.write(respon);
        // 将message返回给客户端
        conn->send(message);
    }
}
// 获取消息对应的处理器
MsgHandler Chatservice::getHandler(int msgid)
{
    // 记录错误日志，msgid没有对应的事件处理回调
    auto it = _msgHandlerMap.find(msgid);
    if (it == _msgHandlerMap.end())
    {
        // 返回一个默认的处理器，空操作
        return [=](const TcpConnectionPtr &conn, Json::Value &js, Timestamp)
        {
            LOG_ERROR << "msgid:" << msgid << " can not find handler!";
        };
    }
    else
    {
        return _msgHandlerMap[msgid];
    }
}

// 客户端异常退出代码业务
void Chatservice::clientCloseException(const TcpConnectionPtr &conn)
{
    User user;
    {
        // 保证线程安全问题
        // 构造时自动加锁，出作用域自动解锁
        lock_guard<mutex> lock(_connmutex);
        for (auto it : _userConnMap)
        {
            if (it.second == conn)
            {
                // 获取异常的用户id
                user.setid(it.first);
                // 删除该用户的链接信息
                _userConnMap.erase(it.first);
                break;
            }
        }
    }

    //取消订阅
    _redismodel.unsubscribe(user.getid());

    // 如果该用户存在
    if (user.getid() != -1)
    {
        cout << user.getid() << endl;
        user.setstate("offline");
        _usrmodel.UpdateState(user);
    }
}

// 消息发送的格式是 msigid  from   to  msg
void Chatservice::oneChat(const TcpConnectionPtr &conn, Json::Value &value, Timestamp timestamp)
{
    int toid = value["toid"].asInt();
    // json转string
    Json::FastWriter fastwriter;
    string message = fastwriter.write(value);
    // 查找要发送给谁,如果在同一台服务器
    {
        lock_guard<mutex> lock(_connmutex);
        auto it = _userConnMap.find(toid);
        if (it != _userConnMap.end())
        {
            it->second->send(message);
            return;
        }
    }

    //如果在不同的服务器，但是在线
    User user=_usrmodel.query(toid);
    if(user.getstate()=="online")
    {
        _redismodel.publish(toid,message);
    }
    // 如果toid不在线，离线存储消息
    _offmsgmdl.insert(toid, message);
}

void Chatservice::reset()
{
    _usrmodel.resetState();
}

void Chatservice::addfriend(const TcpConnectionPtr &conn, Json::Value &js, Timestamp time)
{
    int id = js["id"].asInt();
    int friendid = js["friendid"].asInt();
    _friendmdl.insert(id, friendid);
    _friendmdl.insert(friendid, id);
}

// 创建群组业务
void Chatservice::createGroup(const TcpConnectionPtr &conn, Json::Value &js, Timestamp time)
{
    int useid = js["id"].asInt();
    string name = js["groupname"].asString();
    string desc = js["groupdesc"].asString();
    group gup(-1, name, desc);
    if (_groupmodel.createGroup(gup))
    {
        _groupmodel.addGroup(useid, gup.getid(), "creator");
    }
}

// 加入群组
void Chatservice::addGroup(const TcpConnectionPtr &conn, Json::Value &js, Timestamp time)
{
    int useid = js["id"].asInt();
    int groupid = js["groupid"].asInt();
    _groupmodel.addGroup(useid, groupid, "normal");
}

void Chatservice::showGroup(const TcpConnectionPtr &conn, Json::Value &js, Timestamp time)
{
    int id = js["id"].asInt();
    vector<group> vec = _groupmodel.queryGroups(id);
    Json::Value put;
    put["msgid"]=SHOW_GROUP_ACK;
    if (!vec.empty())
    {
        vector<string> groupv;
        for (auto &gp : vec)
        {
            Json::Value value;
            value["id"] = gp.getid();
            value["name"] = gp.getname();
            value["dec"] = gp.getdec();
            vector<groupuser> gpuser = gp.getgroupuser();
            for (auto &us : gpuser)
            {
                Json::Value people;
                people["userid"] = us.getid();
                people["name"] = us.getname();
                people["state"] = us.getstate();
                people["role"] = us.getrole();
                // 转换为字符串
                Json::FastWriter fastwriter;
                string ap = fastwriter.write(people);
                value["groupuser"].append(ap);
            }
            Json::FastWriter fastwriter;
            string ret = fastwriter.write(value);
            // put["group"].append(ret);
            put["groupmsg"].append(ret);
        }
        Json::FastWriter fastwriter;
        string message = fastwriter.write(put);
        conn->send(message);
    }
}

void Chatservice::showfriends(const TcpConnectionPtr &conn, Json::Value &js, Timestamp time)
{
    int id = js["userid"].asInt();
    // 查询用户的好友信息并返回
    vector<User> uservec = _friendmdl.query(id);
    Json::Value value;
    value["msgid"]=SHOW_FRIENDS_ACK;
    if (!uservec.empty())
    {
        for (auto u : uservec)
        {
            Json::Value us;
            us["id"] = u.getid();
            us["name"] = u.getname();
            us["state"] = u.getstate();
            Json::FastWriter fastwriter;
            string ap = fastwriter.write(us);
            // value["friend"].append(ap);
            value["friend"].append(ap);
        }
    }
    Json::FastWriter fastwrite;
    string ret = fastwrite.write(value);
    conn->send(ret);
}


void Chatservice::leavemessage(const TcpConnectionPtr &conn, Json::Value &js, Timestamp time)
{

    int id=js["userid"].asInt();
    // 查询用户是否有离线消息
    vector<string> vec = _offmsgmdl.query(id);
    Json::Value offmessage;
    offmessage["msgid"]=LEAVE_MESSAGE_ACK;
    if (!vec.empty())
    {
        // 读取消息
        for (auto m : vec)
        {
            //ret["offlinemsg"].append(m);
            offmessage["offlinemsg"].append(m);
        }
        // 删除消息
        _offmsgmdl.remove(id);
    }
    Json::FastWriter fastwriter;
    string ret=fastwriter.write(offmessage);
    conn->send(ret);
}

void Chatservice::groupchat(const TcpConnectionPtr &conn, Json::Value &js, Timestamp time)
{
    int userid=js["userid"].asInt();
    int groupid=js["groupid"].asInt();
    Json::FastWriter fastwriter;
    string message=fastwriter.write(js);
    vector<int> vec=_groupmodel.queryGroupUsers(userid,groupid);
    {
        lock_guard<mutex> lock(_connmutex);
        for(auto id:vec)
        {
            auto it=_userConnMap.find(id);
            //如果两个对象在同一台服务器上
            if(it!=_userConnMap.end())
            {
                it->second->send(message);
            }
            else
            {
                User user=_usrmodel.query(id);
                if(user.getstate()=="online")
                {
                    _redismodel.publish(id,message);
                }
                _offmsgmdl.insert(id,message);
            }
        }
    }
}

void Chatservice::loginout(const TcpConnectionPtr &conn, Json::Value &js, Timestamp time)
{
    int userid=js["id"].asInt();
    {
        lock_guard<mutex> lock(_connmutex);
        auto it=_userConnMap.find(userid);
        if(it!=_userConnMap.end())
        {
            _userConnMap.erase(it);
        }
    }
    //取消订阅
    _redismodel.unsubscribe(userid);
    User user(userid,"","","offline");
    _usrmodel.UpdateState(user);
}



//从Redis消息队列中获取订阅的消息
void Chatservice::handleRedisSubscribeMessage(int userid,string message)
{
    lock_guard<mutex> lock(_connmutex);
    auto it=_userConnMap.find(userid);
    if(it!=_userConnMap.end())
    {
        it->second->send(message);
        return ;
    }

    //存储离线消息
    _offmsgmdl.insert(userid,message);
}